package org.dbpt.performance;

import java.sql.Connection;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;

import javax.sql.DataSource;

public class SQLItem extends MeasureProviderAdapter implements DBPTItem, ConnectionConsumer {
	private DataSource dataSource;
	private Connection connection;
	private String sql;
	private boolean useParamMetadata = true;
	private ThreadLocal<PreparedStatement> preparedStatement = new ThreadLocal<PreparedStatement>();
	private List<ParamGenerator> paramGenerators;
	private boolean suppressException = true;
	
	public boolean isSuppressException() {
		return suppressException;
	}

	public void setSuppressException(boolean suppressException) {
		this.suppressException = suppressException;
	}

	public List<ParamGenerator> getParamGenerators() {
		return paramGenerators;
	}

	public void setParamGenerators(List<ParamGenerator> paramGenerators) {
		this.paramGenerators = paramGenerators;
	}

	public boolean isUseParamMetadata() {
		return useParamMetadata;
	}

	public void setUseParamMetadata(boolean useParamMetadata) {
		this.useParamMetadata = useParamMetadata;
	}

	public DataSource getDataSource() {
		return dataSource;
	}

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	public String getSql() {
		return sql;
	}

	public void setSql(String sql) {
		this.sql = sql;
	}

	@Override
	public void prepare() {
		Connection con = null;
		try {
			if(con == null) {
				con = dataSource.getConnection();
			}
			preparedStatement.set(con.prepareStatement(sql));
		} catch (SQLException e) {
			if(!suppressException)
				throw new RuntimeException("Cannot prepare statement", e);
			e.printStackTrace();
		}
	}
	
	protected PreparedStatement getPreparedStatement() {
		PreparedStatement ps = preparedStatement.get();
		if(ps == null) {
			prepare();
			ps = preparedStatement.get();
		}
		return ps;
	}

	public Connection getConnection() {
		return connection;
	}

	public void setConnection(Connection connection) {
		this.connection = connection;
	}

	@Override
	public void doIteration() {
		PreparedStatement localPreparedStatement = getPreparedStatement();
		try {
			if(useParamMetadata) {
				ParameterMetaData metadata = localPreparedStatement.getParameterMetaData();
				int paramCount = metadata.getParameterCount();
				for (int j = 0; j < paramCount; j++) {
					int genIdx = j;
					if(genIdx >= paramGenerators.size()) 
						genIdx = paramGenerators.size() - 1;
					paramGenerators.get(genIdx).generate(localPreparedStatement, j + 1);
				}
			} else if(paramGenerators != null){
				int j = 0;
				for (ParamGenerator paramGenerator : paramGenerators) {
					paramGenerator.generate(localPreparedStatement, j + 1);
					j++;
				}
			}
			localPreparedStatement.execute();
		}catch (SQLException e) {
			if(!suppressException)
				throw new RuntimeException("Cannot execute statement", e);
			System.err.println("Cannot execute query : " + sql);
			e.printStackTrace();
		}
	}

	@Override
	public void cleanup() {
		try {
			preparedStatement.get().close();
		} catch (SQLException e) {
			if(!suppressException)
				throw new RuntimeException("Cannot close prepared statement", e);
			e.printStackTrace();
		}
	}
}
